<?php

namespace PhpYes\Com;


final class RedisManager
{
    /** @var RedisManager */
    private static $singleObj = null;
    /** @var array */
    private $config;
    /** @var string|null */
    private $defaultName;
    /** @var array */
    private $name2RedisConfigArr;
    /** @var array */
    private $name2RedisArr;

    /**
     * @param array $config
     */
    public static function init($config)
    {
        self::$singleObj = new RedisManager($config);
    }

    /**
     * @return RedisManager
     */
    public static function getSingleObj()
    {
        return self::$singleObj;
    }

    /**
     * RedisManager constructor.
     * @param array $config
     */
    private function __construct($config)
    {
        $this->config = $config;
        $this->name2RedisConfigArr = array();
        $this->name2RedisArr = array();
    }

    /**
     * @return array
     */
    public function getConfig()
    {
        return $this->config;
    }

    /**
     * @param string|null $name
     * @return RedisConfig|null
     */
    public function getRedisConfig($name = null)
    {
        if (empty($name)) {
            $name = $this->getDefaultName();
            if (empty($name)) {
                return null;
            }
        }

        if (empty($this->name2RedisConfigArr[$name])) {
            if (empty($this->config[$name])) {
                return null;
            }
            $this->name2RedisConfigArr[$name] = new RedisConfig($name, $this->config[$name]);
        }

        return $this->name2RedisConfigArr[$name];
    }

    /**
     * @param string|null $name
     * @return \Redis|null
     * @throws HttpException
     */
    public function getRedis($name = null)
    {
        if (empty($name)) {
            $name = $this->getDefaultName();
            if (empty($name)) {
                return null;
            }
        }

        if (empty($this->name2RedisArr[$name])) {
            $redisConfig = $this->getRedisConfig($name);
            if (empty($redisConfig)) {
                return null;
            }

            $redis = new \Redis();
            if (!$this->connect($redis, $redisConfig)) {
                throw new HttpException("redis {$name} 连接失败", 500);
            }
            $this->name2RedisArr[$name] = $redis;
        }

        return $this->name2RedisArr[$name];
    }

    /**
     * @return null|string
     */
    public function getDefaultName()
    {
        if ($this->defaultName) {
            return $this->defaultName;
        }

        $config = $this->config;
        if (empty($config)) {
            return null;
        }

        foreach ($config as $n => $c) {
            if (is_array($c) && !empty($c['default'])) {
                $this->defaultName = $n;

                return $this->defaultName;
            }
        }

        if (!empty($config['default'])) {
            if (is_string($config['default'])) {
                $this->defaultName = $config['default'];

                return $this->defaultName;
            } else {
                $this->defaultName = 'default';

                return $this->defaultName;
            }
        }

        $this->defaultName = array_keys($config)[0];

        return $this->defaultName;
    }

    /**
     * 幂等连接
     * redis 初次连接时可以使用这个连接，后面使用过程中怀疑 redis 断开了也可以使用这个重新连接
     * @param \Redis $redis
     * @param RedisConfig $redisConfig
     * @return bool|string
     */
    public function connect(\Redis $redis, RedisConfig $redisConfig)
    {
        try {
            $isOk = $redis->ping();
        } catch (\Exception $e) {
            $isOk = false;
        }
        if ($isOk) {
            return $isOk;
        }

        $host = $redisConfig->getHost();
        if (empty($host)) {
            return false;
        }

        $isOk = $redis->connect($host, $redisConfig->getPort(), $redisConfig->getTimeout());
        if (!$isOk) {
            $isOk = $redis->connect($host, $redisConfig->getPort(), $redisConfig->getTimeout());
            if (!$isOk) {
                $isOk = $redis->connect($host, $redisConfig->getPort(), $redisConfig->getTimeout());
            }
        }
        $password = $redisConfig->getPassword();
        if ($isOk && $password) {
            if (!$redis->auth($password)) {
                if (!$redis->auth($password)) {
                    $redis->auth($password);
                }
            }
        }

        return $isOk;
    }

}